/**
  * @file 		osport.c
  * @brief		多核通信管理模块、支持全双工多端口收发
  *
  * @copyright  Copyright (c) 2020~2030 ShenZhen Dxtc Technology Co., Ltd. 
  * All rights reserved.
  *
  * @version	v3.1
  * @author		FengLi
  * @date 	    2021-10-28
  *
  * @note		鼎新同创·智能锁
  * 
  * @par 修改日志:
  * <1> 2021/08/02 v3.1 FengLi 创建初始版本
  *******************************************************************/
#include "osport.h"
//#include "co_debug.h"//

/** @brief   osport.c文件日志打印宏定义 */
#define OSPORT_LOG(format, ...)  __OSAL_LOG("[osport.c] " C_CYAN format C_NONE, ##__VA_ARGS__)

/** @brief   断言 */
#define OSPORT_ASSERT( x ) if ((x) == 0) { __OSAL_LOG("[osport.c:%05d] ASSERT\r\n", __LINE__); Device_EnterCritical(); for( ;; ); }

#if (OSPORT_PACKET_MAX_LEN >= 1024)
#define OSPORT_RX_BUFF_LEN     (OSPORT_PACKET_MAX_LEN * 2) ///< 接收缓存区最大长度 
#else
#define OSPORT_RX_BUFF_LEN     (OSPORT_PACKET_MAX_LEN * 1) ///< 接收缓存区最大长度 
#endif

///< 收发的队列长度
#define OSPORT_QUEUE_LEN       (30)

///< 重发次数
#ifndef KOS_PARAM_OSPORT_RESEND_TIMES
#define OSPORT_RESEND_TIMES    (5)
#else
#define OSPORT_RESEND_TIMES    KOS_PARAM_OSPORT_RESEND_TIMES
#endif

///< 重发间隔Ms
#ifndef KOS_PARAM_OSPORT_RESEND_INTERVAL
#define OSPORT_RESEND_INTERVAL (20) 
#else
#define OSPORT_RESEND_INTERVAL KOS_PARAM_OSPORT_RESEND_INTERVAL
#endif

#define OSPORT_RXPSN_BUFF_LEN  (10) ///< PSN的缓存数量

#pragma pack(1)
/** @brief  多核通信收发数据包结构类型  */
typedef struct 
{
    uint16_t head;     ///< 包头
    uint16_t crc16;    ///< 校验
    uint16_t len;      ///< 数据长度
    uint8_t  cmd;      ///< 命令字
    uint8_t  psn;      ///< 包编号

#ifdef KOS_PARAM_OSPORT_CRYPTO
    uint8_t  insert;   ///< 在payload末尾插入的数据个数（当明文传输时：其值为0）
                       ///< 1、当payload加密传输时，必须要在payload末尾插入2字节crc用来做校验
                       ///< 2、给payload区域做AES加解密必须要16字节对齐，所以会在payload末尾插入0~15字节来补齐
#endif

    uint8_t  payload[];///< 数据载荷（如果加密传输时：长度必须为16的整数倍）
}OsPortPaket_stu_t;
#pragma pack()

/** @brief  多核通信发送队列的数据域结构类型  */
typedef struct
{
    uint16_t          resend_times; ///< 重发次数
    uint32_t          timestamp;    ///< 发送时间戳
    OsPortPaket_stu_t packet;       ///< 数据包
}OsPortTxInfo_stu_t;

/** @brief  接收时的相关信息（PSN）  */
typedef struct
{
    uint8_t  sn;         ///< 包序号
    uint32_t timestamp;  ///< 时间戳
}OsPortRxPsn_stu_t;

/** @brief  多核通信核心数据管理结构类型  */
typedef struct
{
    SuperQueueHandle_t     tx_queue;    ///< 发送队列
    SuperQueueHandle_t     rx_queue;    ///< 接收队列
    RingBufferHandle_t     rx_buffer;   ///< RX数据流缓存
    VirtualHardware_enum_t hwl_port;    ///< 硬件端口
    VirtualHardware_enum_t hwl_wake_pin;///< 硬件端口唤醒脚

    OsPortRxPsn_stu_t  rx_psn[OSPORT_RXPSN_BUFF_LEN]; ///< 最近收到的10个数据包的PSN
    uint8_t            tx_psn;                        ///< 发出数据包的PSN
    FlagStatus         state;                         ///< 端口状态

#ifdef KOS_PARAM_OSPORT_CRYPTO
    uint8_t account_id;
    uint32_t rm;
    uint32_t rs;
    uint32_t state_timestamp;
#endif

    uint32_t rx_parse_timestamp;                      ///< 开始解析的时刻（收到包头的时刻）
    uint16_t rx_parse_cnt;                            ///< 解析计数
    uint16_t rx_parse_temp;                           ///< 解析临时变量
    uint8_t  rx_parse_buf[OSPORT_PACKET_MAX_LEN];     ///< 解析缓存
}OsPort_stu_t;

#if (OSPORT_PACKET_MAX_LEN >= 1024)
/** @brief  multi-pack0 数据域内容  */
typedef struct
{
    uint8_t  msg_cmd;  //消息命令
    uint8_t  msg_id;   //消息ID
    uint16_t msg_len;  //消息总长度
    uint16_t pack_len; //分包消息长度
}OsPortMultiPack0_stu_t;
static uint8_t multi_pack_msg_id = 1;       ///< multi-pack 消息ID
#endif

static volatile uint16_t tx_queue_len;      ///< 当前tx队列已缓存的节点数量
static volatile uint16_t rx_queue_len;      ///< 当前rx队列已缓存的节点数量

static OsPort_stu_t *p_osport = NULL;       ///< 定义一个指向多核通信核心数据管理的结构体指针,用于指向“OsPort_stu_t”结构
static uint8_t osport_qty = 0;              ///< OS端口数量（具体数量由底层驱动决定、OS端口用来与扩展CPU（子系统 ）通信）

/** @brief   处理OSPROT收到的命令 -- 从机端 */
extern void OSAL_ProcessOsportCmd(VirtualHardware_enum_t port, uint8_t cmd, uint8_t *data, uint16_t len);
extern void OSAL_OsportStateChange(VirtualHardware_enum_t port, FlagStatus status);

//! ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//! ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef KOS_PARAM_OSPORT_CRYPTO

#define OSPORT_ACCOUNT_MAX_QTY    10  //< 最多10个账户
#define OSPORT_ACCOUNT_SAVE_ADDR  16  //< 账户表存储的首地址
/**
 * @brief 账户列表
 * 
 * 1、账户0用来存储本地账号信息（本地自己的ESN、PWD），由产测进行添加
 * 2、账户1~9用来存储对端设备的账号信息，用阿泼泼走子设备添加流程进行添加
 * 3、默认KOS主从之间的所有端口通信都使用账号0
 * 4、KOS从机可以通过ProbeRequest命令告诉主锁，用指定账户进行通信
 */

#pragma pack(1)
static struct 
{
    uint8_t devNum;    //< 该账户对应的设备类型
    uint8_t esn[14];   //< 账户的ESN
    uint8_t pwd1[12];  //< 账户的PWD
    uint8_t dev_info[10];
}account_list[OSPORT_ACCOUNT_MAX_QTY];
#pragma pack()


//< 标记当前正在添加的账户索引
static uint8_t add_account_index = 0;

//< 记录当前是哪个应用在添加子设备
static uint16_t add_account_app;

//< 手机阿泼泼添加账户（添加子设备）时注册的回调
static void (*add_account_cb)(ErrorStatus) = NULL;


/**
  * @brief  给指定端口创建Rs --- 从机端调用
  *         
  * @return 返回Rs
  */
uint32_t OSAL_OsportCreateRs(VirtualHardware_enum_t port, uint32_t rm)
{
    for (int i = 0; i < osport_qty; i++)
    {
        if (p_osport[i].hwl_port == port)
        {
            if (p_osport[i].rm != rm)
            {
                p_osport[i].rm = rm;
                OSAL_Crypto(CALC_RANDOM_NUMBER, NULL, 4, &p_osport[i].rs, NULL);
            }
            return p_osport[i].rs;
        }
    }
    return 0;
}

/**
  * @brief  给指定端口创建Rm --- 主机端调用
  *         
  * @return 返回Rm
  */
uint32_t OSAL_OsportCreateRm(VirtualHardware_enum_t port, uint32_t number)
{
    static uint32_t temp[OSPORT_MAX_QTY] = {0};

    for (int i = 0; i < osport_qty; i++)
    {
        if (p_osport[i].hwl_port == port)
        {
            if (temp[i] != number)
            {
                temp[i] = number;
                OSAL_Crypto(CALC_RANDOM_NUMBER, NULL, 4, &p_osport[i].rm, NULL);
                p_osport[i].rs = 0;
            }
            return p_osport[i].rm;
        }
    }
    return 0;
}

/**
  * @brief  主机端握手结束：保存Rm/Rs -- 主机端调用
  *         
  * @return 无
  */
void OSAL_OsportSaveRmRs(VirtualHardware_enum_t port, uint32_t rm, uint32_t rs)
{
    for (int i = 0; i < osport_qty; i++)
    {
        if (p_osport[i].hwl_port == port)
        {
            if (rm == p_osport[i].rm)
            {
                p_osport[i].rs = rs;
                OSPORT_LOG("Save Rm/Rs success\r\n");
            }

            if ((p_osport[i].account_id == add_account_index) && (add_account_index > 0))
            {
                OSPORT_LOG("Add account result:%d, esn:%s\r\n", (rm == p_osport[i].rm), account_list[add_account_index].esn);
                if (add_account_cb != NULL)
                {
                    extern uint16_t g_ActiveAppIndex;
                    uint16_t temp = g_ActiveAppIndex;

                    g_ActiveAppIndex = add_account_app;
                    add_account_cb((ErrorStatus)(rm == p_osport[i].rm));
                    g_ActiveAppIndex = temp;
                }

                if (rm == p_osport[i].rm)
                {
                    /* 新账户添加成功：存NV */
                    Device_Write(vEEPROM_0,
                                 &account_list[add_account_index],
                                 sizeof(account_list[0]),
                                 OSPORT_ACCOUNT_SAVE_ADDR + add_account_index * sizeof(account_list[0]));
                    add_account_index = 0;
                    add_account_cb = NULL;
                }
            }
            break;
        }
    }
}

/**
  * @brief  根据端口获取账号表中对应的esn和devNum
  *         
  * @return 返回删除结果
  */
uint8_t OSAL_OsportSearchAccountInfo(VirtualHardware_enum_t port, uint8_t *esn)
{
    uint8_t id_index = 0;
    uint8_t devNum;

    for (int i = 0; i < osport_qty; i++)
    {
        if (p_osport[i].hwl_port == port)
        {
            id_index = p_osport[i].account_id;
            memcpy(esn,account_list[id_index].esn,sizeof(account_list[id_index].esn));
            devNum = account_list[id_index].devNum;
            return devNum;
        }

    }

    return 0;
}

/**
  * @brief  设置账号表中设备信息
  *         
  * @return 返回结果
  */
ErrorStatus OSAL_OsportSetAccountDevInfo(uint8_t *esn,void *data)
{
    for (int i = 1; i < OSPORT_ACCOUNT_MAX_QTY; i++)
    {
        if (memcmp(account_list[i].esn, esn, 13) == 0)
        {
            memcpy(&account_list[i].dev_info,(uint8_t *)data,10);

            Device_Write(vEEPROM_0,
                         &account_list[i],
                         sizeof(account_list[0]),
                         OSPORT_ACCOUNT_SAVE_ADDR + i * sizeof(account_list[0]));
            return SUCCESS;
        }
    }
    return ERROR;
}

/**
  * @brief  获取账号表中设备信息
  *         
  * @return 返回结果
  */
ErrorStatus OSAL_OsportGetAccountDevInfo(uint8_t *esn,void *data)
{
    for (int i = 1; i < OSPORT_ACCOUNT_MAX_QTY; i++)
    {
        if (memcmp(account_list[i].esn, esn, 13) == 0)
        {
            memcpy(data,account_list[i].dev_info,sizeof(account_list[i].dev_info));

            return SUCCESS;
        }
    }
    return ERROR;
}


/**
  * @brief  阿泼泼删除一个账户（删除子设备）
  *         
  * @return 返回删除结果
  */
ErrorStatus OSAL_OsportDeleteAccount(uint8_t devNum, uint8_t *esn)
{
    /* 清空全部子设备 */
    if (devNum == 0 && esn == NULL)
    {
        memset(&account_list[1], 0, sizeof(account_list[0]) * (OSPORT_ACCOUNT_MAX_QTY - 1));
        Device_Write(vEEPROM_0,
                     &account_list[1],
                     sizeof(account_list[0]) * (OSPORT_ACCOUNT_MAX_QTY - 1),
                     OSPORT_ACCOUNT_SAVE_ADDR + (1 * sizeof(account_list[0])));
        return SUCCESS;
    }

    /* 删除指定子设备 */
    for (int i = 1; i < OSPORT_ACCOUNT_MAX_QTY; i++)
    {
        if (account_list[i].devNum == devNum && memcmp(account_list[i].esn, esn, 13) == 0)
        {
            memset(&account_list[i], 0, sizeof(account_list[0]));
            Device_Write(vEEPROM_0,
                         &account_list[i],
                         sizeof(account_list[0]),
                         OSPORT_ACCOUNT_SAVE_ADDR + i * sizeof(account_list[0]));
            return SUCCESS;
        }
    }
    return ERROR;
}

/**
  * @brief  阿泼泼添加一个账户（添加子设备）
  *         
  * @return 返回添加结果
  */
ErrorStatus OSAL_OsportAddAccount(uint8_t devNum, uint8_t *esn, uint8_t *pwd1, void (*add_cb)(ErrorStatus))
{
    uint8_t index = OSPORT_ACCOUNT_MAX_QTY;

    if (devNum == 0)
    {
        return ERROR;
    }

    /* 1、查重 */
    for (int i = 1; i < OSPORT_ACCOUNT_MAX_QTY; i++)
    {
        if (account_list[i].devNum == devNum)
        {
            if (memcmp(esn, account_list[i].esn, 13) == 0)
            {
                return ERROR; //< 该设备已经添加过
            }
            if (devNum != 12) //< DFU_DEVNUM_KEYFOB（12）遥控器类型的子设备允许添加多个
            {
                return ERROR; //< 非遥控器类型的子设备只允许添加一个
            }
        }
    }

    /* 2、查找空闲位置 */
    if (add_account_index == 0)
    {
        for (int i = 1; i < OSPORT_ACCOUNT_MAX_QTY; i++)
        {
            if (account_list[i].devNum == 0)
            {
                index = i;
                break;
            }
        }
    }
    else
    {
        index = add_account_index;
    }

    /* 3、添加账户 */
    if (index < OSPORT_ACCOUNT_MAX_QTY)
    {
        extern uint16_t g_ActiveAppIndex;

        account_list[index].devNum = devNum;
        memcpy(account_list[index].esn, esn, 13);
        memcpy(account_list[index].pwd1, pwd1, 12);
        memset(&account_list[index].dev_info,0,10);
        add_account_cb = add_cb;   //< 回调指针
        add_account_index = index; //< 标记当前正在添加的账户索引
        add_account_app = g_ActiveAppIndex;
        return SUCCESS;
    }
    return ERROR;
}

/**
  * @brief  设置默认账户 -- 应用层产测调用
  *         
  * @return 返回设备devNum
  */
void OSAL_OsportSetDefaultAccount(uint8_t devNum, uint8_t *esn, uint8_t *pwd1)
{
    account_list[0].devNum = devNum;
    memcpy(account_list[0].esn, esn, 13);
    memcpy(account_list[0].pwd1, pwd1, 12);
    Device_Write(vEEPROM_0, &account_list[0], sizeof(account_list[0]), OSPORT_ACCOUNT_SAVE_ADDR);
    
    //TODO default账户信息同步给所有KOS子设备（有线连接的子设备）
}

/**
  * @brief  读取默认账户 -- 子设备底层调用
  *         
  * @param[out]  esn：设备esn
  * 
  * @return 返回设备devNum
  */
uint8_t OSAL_OsportGetDefaultAccount(uint8_t *esn)
{
    memcpy(esn, account_list[0].esn, 13);
    return account_list[0].devNum;
}

/**
  * @brief  Probe请求
  *         
  * @param  dev_type：请求建立连接的虚拟设备类型 DeviceType_enum_t
  * @param  devNum：子设备devNum
  * @param  esn：子设备esn
  * 
  * @return 返回具体的虚拟设备 VirtualHardware_enum_t
  */
uint16_t OSAL_OsportProbeRequest(uint16_t dev_type, uint8_t devNum, uint8_t *esn)
{
    uint8_t index = OSPORT_ACCOUNT_MAX_QTY;

    /* 判断这个ESN是否在账户表里面 */
    for (int i = 1; i < OSPORT_ACCOUNT_MAX_QTY; i++)
    {
        if (account_list[i].devNum == 0)
        {
            continue;
        }

        if ((account_list[i].devNum == devNum) && (memcmp(account_list[i].esn, esn, 13) == 0))
        {
            index = i;
            break;
        }
    }
    if (index == OSPORT_ACCOUNT_MAX_QTY)
    {
        OSPORT_LOG("Probe error: illegal esn\r\n");
        return vNOHARDWARE;
    }
    
    /* 判断当前是否已经连接了同类型的设备 */
    for (int i = 0; i < osport_qty; i++)
    {
        if (p_osport[i].state == SET && account_list[p_osport[i].account_id].devNum == devNum)
        {
            OSPORT_LOG("Probe error: This devNum is connected\r\n");
            return vNOHARDWARE;
        }
    }

    /* 判断当前是否有空闲（离线）的端口 */
    for (int i = 0; i < osport_qty; i++)
    {
        if ((p_osport[i].state == RESET) && ((uint16_t)(p_osport[i].hwl_port >> 8) == dev_type))
        {
            p_osport[i].state = SET;
            p_osport[i].account_id = index;
            return p_osport[i].hwl_port;
        }
    }
    OSPORT_LOG("Probe error\r\n");
    return vNOHARDWARE;
}

/**
  * @brief  加解密通信数据
  * @details 详细说明：OSPORT收到的数据用本函数解密、发出的数据用本函数加密
  *         
  * @return NULL
  */
static void OSAL_OsportCryptoData(Crypto_enum_t crypto, uint8_t cmd, uint8_t port_index, uint8_t *data, uint16_t len)
{
    uint8_t key[16] = {0};
    uint8_t account_id = p_osport[port_index].account_id;

    OSPORT_ASSERT((len & 0x0F) == 0);
    OSPORT_ASSERT(len != 0);
    
    if (cmd == OSPORT_CMD_HELLO || cmd == OSPORT_CMD_SYNC)
    {
        //< 握手阶段：用PWD1+4字节0作为KEY
        memcpy(key, account_list[account_id].pwd1, 12);
    }
    else
    {
        //< 正常通信：用PWD1+RS+RM经过MD5运算后，得到的16字节作为KEY
        uint8_t temp[20] = {0};
        memcpy(temp, account_list[account_id].pwd1, 12);
        memcpy(temp+12, &p_osport[port_index].rm, 4);
        memcpy(temp+16, &p_osport[port_index].rs, 4);
        OSAL_Crypto(CALC_MD5, temp, 20, key, NULL);
    }

    for (int i = 0; i < len; i += 16)
    {
        uint8_t src[16];
        
        memcpy(src, &data[i], 16);
        OSAL_Crypto(crypto, src, 16, &data[i], key);
    }
    // OSPORT_LOG("Crypto account_id:%d, esn:%s, key: ", account_id, account_list[account_id].esn);
    // for (int i = 0; i < 16; i++)
    // {
    //     __OSAL_LOG("%02X", key[i]);
    // }
    // __OSAL_LOG("\r\n");
}

/**
  * @brief  账户表初始化
  * @details 详细说明:
  *
  * @return NULL
  */
static void OSAL_OsportAccountInit(void)
{
    uint32_t init_flag = 0;

    Device_Read(vEEPROM_0, &init_flag, sizeof(init_flag), 0);
    if (init_flag != UINT32_FLAG)//< 首次使用清空nv
    {
        init_flag = UINT32_FLAG;
        Device_Write(vEEPROM_0, &init_flag, sizeof(init_flag), 0);

        memset(account_list, 0, sizeof(account_list));
        Device_Write(vEEPROM_0, account_list, sizeof(account_list), OSPORT_ACCOUNT_SAVE_ADDR);
        OSPORT_LOG("Reset account list\r\n");
    }
    else
    {
        Device_Read(vEEPROM_0, account_list, sizeof(account_list), OSPORT_ACCOUNT_SAVE_ADDR);
        for (int i = 0; i < OSPORT_ACCOUNT_MAX_QTY; i++)
        {
            if (account_list[i].devNum != 0)
            {
                OSPORT_LOG("Account_list[%d]: ESN(%s) devNum(%d)\r\n", i, account_list[i].esn, account_list[i].devNum);
            }
        }
    }
}

ErrorStatus OSAL_OsportGetAccountListInfo(uint8_t index,uint8_t *esn,uint8_t *dev_info)
{
    uint8_t i;

    for (int i = index; i < OSPORT_ACCOUNT_MAX_QTY; i++)
    {
        if (account_list[i].devNum != 0)
        {
            memcpy(esn,account_list[i].esn,13);
            memcpy(dev_info,account_list[i].dev_info,10);
            OSPORT_LOG("Account_list[%d]: ESN(%s) devNum(%d)\r\n", i, account_list[i].esn, account_list[i].devNum);
            return SUCCESS;
        }
    }

    return ERROR;
}


/**
  * @brief   查询账号表指定的设备类型是否绑定了
  */
ErrorStatus OSAL_OsportInquireAccountListDevnum(uint8_t devnum)
{

    for (int i = 0; i < OSPORT_ACCOUNT_MAX_QTY; i++)
    {
        if (account_list[i].devNum == devnum)
        {
            OSPORT_LOG("Inquire Account_list[%d]: ESN(%s) devNum(%d) is bind\r\n", i, account_list[i].esn, account_list[i].devNum);
            return SUCCESS;
        }
    }

    return ERROR;
}

/**
  * @brief   端口状态管理
  */
static void OSAL_OsportStateManage(void)
{
    static uint32_t last_state[OSPORT_MAX_QTY] = {0};

    for (int i = 0; i < osport_qty; i++)
    {
        if (last_state[i] != p_osport[i].state_timestamp)
        {
            last_state[i] = p_osport[i].state_timestamp;
            OSAL_OsportStateChange(p_osport[i].hwl_port, p_osport[i].state);
            if (p_osport[i].state == RESET)
            {
                p_osport[i].rm = 0;
                p_osport[i].rs = 0;
            }
        }
    }
}

/**
  * @brief   重新发起连接（扫描广播连接）
  *         
  * @details 详细说明:BLE类型端口
  */
void OSAL_OsportReconnect(void)
{
    OSPORT_LOG("Osport reconnect\r\n");
    Device_Write(p_osport[0].hwl_port, NULL, 0, BLE_CTRL_RECONNECT);
}

/**
  * @brief   产测广播
  *         
  * @details 详细说明:BLE类型端口
  */
void OSAL_OsportAdv(void)
{
    OSPORT_LOG("Osport adv\r\n");
    Device_Write(p_osport[0].hwl_port, account_list[0].esn, 13, BLE_CTRL_SET_ADV);    
}

/**
  * @brief   终止连接
  *         
  * @details 详细说明:BLE类型端口
  */
void OSAL_OsportDisconnect(void)
{
    OSPORT_LOG("Osport disconnect\r\n");
    Device_Write(p_osport[0].hwl_port, NULL, 0, BLE_CTRL_DISCONNECT);
}

/**
  * @brief   产测，蓝牙频偏校准
  *         
  * @details 详细说明:BLE类型端口
  */
void OSAL_OsportCalibration(uint8_t *data)
{
    Device_Write(p_osport[0].hwl_port, data, 9, BLE_CTRL_CALIBRATION);
}

/**
  * @brief   产测，读蓝牙gett和频偏
  *         
  * @details 详细说明:BLE类型端口
  */
void OSAL_OsportGetBleGatt(uint8_t *data)
{
    Device_Read(p_osport[0].hwl_port, data, 3, 0);
}

/**
  * @brief   产测，读蓝牙Mac
  *         
  * @details 详细说明:BLE类型端口
  */
void OSAL_OsportGetBleMac(uint8_t *data)
{
    Device_Read(p_osport[0].hwl_port, data, 6, 1);
}


#endif
//! ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//! ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//! ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
  * @brief  硬件RX中断回调
  * @details 详细说明:
  *         
  * @param[in]  data：接收到的数据缓存指针
  * @param[in]  len：数据长度
  *         
  * @return NULL
  */
static void OSAL_OsPortRxCallback(VirtualHardware_enum_t dev, void *data, uint32_t len)
{
    uint16_t data_type = len >> 16;
    uint16_t data_len  = len & UINT16_MAX;

    for (int index = 0; index < osport_qty; index++)
    {
        if (p_osport[index].hwl_port == dev)
        {
            if (data_type == PUBLISH_BLE_STATUS)
            {
                p_osport[index].state = (FlagStatus)(*((uint8_t*)data));
                #if defined(KOS_PARAM_OSPORT_CRYPTO)
                p_osport[index].state_timestamp = OSAL_GetTickCount();
                #endif
            }
            else if ((data_type == PUBLISH_APP_CMD) && (data_len > 0))
            {
                for (int i = 0; i < len; i++)
                {
                    OSAL_RingBufferWrite(p_osport[index].rx_buffer, (uint8_t *)data + i);
                }
                Device_TrigPendSV();
            }
            break;
        }
    }
}

/**
  * @brief  计算CRC16校验
  * @details 详细说明:主要将待处理的数据进行CRC16校验处理
  *         
  * @param[in]  puchMsg：数据指针
  * @param[in]  usDataLen：数据长度
  *         
  * @return 返回生成CRC16校验的数值
  */
static uint16_t OSAL_OsPortCrc16(uint8_t *puchMsg, uint32_t usDataLen)
{
    unsigned short wCRCin = 0x0000;
    unsigned short wCPoly = 0x1021;
    unsigned char wChar = 0;

    while (usDataLen--)
    {
        wChar = *(puchMsg++);
        wCRCin ^= (wChar << 8);

        for(int i = 0; i < 8; i++)
        {
            if(wCRCin & 0x8000)
            {
                wCRCin = (wCRCin << 1) ^ wCPoly;
            }
            else
            {
                wCRCin = wCRCin << 1;
            }
        }
    }
    return (wCRCin) ;
}

/**
  * @brief  从接收缓存里面，解析一个数据包出来
  * @details 详细说明:无
  *         
  * @param[in]  osport：当前端口数据结构
  *         
  * @return 返回数据包指针
  */
static uint8_t *OSAL_OsPortGetOnPacket(OsPort_stu_t *osport)
{
    int tmpLen = OSAL_RingBufferGetValidSize(osport->rx_buffer);

    for (; tmpLen > 0; tmpLen--)
    {
        uint8_t data = 0;    

        OSAL_RingBufferRead(osport->rx_buffer, &data);
        osport->rx_parse_temp <<= 8;
        osport->rx_parse_temp |= data;

        if (osport->rx_parse_temp == 0xFFFF)
        {
            memcpy(osport->rx_parse_buf, &(osport->rx_parse_temp), 2);
            osport->rx_parse_cnt = 2;
            osport->rx_parse_timestamp = OSAL_GetTickCount(); //< 记录收到包头的时刻
        }
        else if (osport->rx_parse_temp == 0xFF55)
        {
            ;//< 丢弃0XFF55这种数据
        }
        else if (osport->rx_parse_cnt >= 2)
        {
            osport->rx_parse_buf[osport->rx_parse_cnt++] = data;
            if (osport->rx_parse_cnt >= sizeof(OsPortPaket_stu_t))
            {
                OsPortPaket_stu_t *packet = (OsPortPaket_stu_t *)(osport->rx_parse_buf);
                if (osport->rx_parse_cnt == packet->len + 6)
                {
                    //printf(packet);
                    osport->rx_parse_cnt = 0;

                    if (OSAL_OsPortCrc16(&(packet->cmd), packet->len) == packet->crc16)
                    {
                        return osport->rx_parse_buf;
                    }
                    else
                    {
                        OSPORT_LOG("crc err\r\n");
                    }
                }
                else if (osport->rx_parse_cnt >= OSPORT_PACKET_MAX_LEN)
                {
                    OSPORT_LOG("len err\r\n");
                    osport->rx_parse_cnt = 0;
                }
            }
        }
    }
    return NULL;
}

/**
  * @brief   写一个数据包到设备驱动层
  * @details 详细说明:数据包里面有0xFF的数据，会在其后面插入0x55（包头除外）
  *         
  * @param[in]  port：端口
  * @param[in]  in_data：需要发出的数据
  * @param[in]  len：数据长度
  *         
  * @return NULL
  */
static int OSAL_OsPortWriteData2Hwl(VirtualHardware_enum_t port, void *in_data, uint16_t len)
{
    uint16_t num = 0, j = 0;
    uint8_t *packet, *data;
    int ret;

    data = (uint8_t *)in_data;
    for (int i = 0; i < len; i++)
    {
        if (data[i] == 0XFF && i > 0)
        {
            num++;
        }
    }
    packet = (uint8_t *)OSAL_Malloc(len + num);
    OSPORT_ASSERT(packet != NULL);

    for (int i = 0; i < len; i++)
    {
        packet[j++] = data[i];
        if (data[i] == 0XFF && i > 0)
        {
            packet[j++] = 0x55;
        }
    }
    ret = Device_Write(port, packet, len + num, BLE_CTRL_DATA);
    OSAL_Free( packet );
    return ret;
}

/**
  * @brief   发送一个ACK包
  * @details 详细说明:无
  *         
  * @param[in]  port：端口
  * @param[in]  cmd：命令
  * @param[in]  psn：包编号
  *         
  * @return NULL
  */
static void OSAL_OsPortSendAck(VirtualHardware_enum_t port, uint8_t cmd, uint8_t psn)
{
    OsPortPaket_stu_t ack = {
        .head = 0xFFFF,
        .len = sizeof(OsPortPaket_stu_t) - 6,
        .cmd = cmd,
        .psn = psn,
    };
    ack.crc16 = OSAL_OsPortCrc16(&(ack.cmd), ack.len);
    OSAL_OsPortWriteData2Hwl(port, &ack, sizeof(OsPortPaket_stu_t));
}

/**
  * @brief   检查RX-PSN
  * @details 详细说明:当前收到的rxsn最近有收到过：判定为重发包
  *         
  * @param[in]  psn_list：过去一段时间内收到的所有RX-PSN
  * @param[in]  rxsn：当前收到的包的sn
  * @param[in]  rx_time：当前收到的包的时间戳
  *         
  * @return 当前收到的包属于重发包:返回SUCCESS
  */
static ErrorStatus OSAL_OsPortCheckRxPsn(OsPortRxPsn_stu_t *psn_list, uint8_t rxsn, uint32_t rx_time)
{
    for (int i = 0; i < OSPORT_RXPSN_BUFF_LEN; i++)
    {
        if (rxsn == psn_list[i].sn)
        {
            if (OSAL_PastTime(rx_time, psn_list[i].timestamp) <= (OSPORT_RESEND_INTERVAL * OSPORT_RESEND_TIMES + 100))
            {
                return SUCCESS;
            }
        }
    }
    return ERROR;
}

/**
  * @brief   保存当前收到的RX-PSN
  * @details 详细说明:将PSN表里面最旧的一个PSN替换成当前收到的SN，并记录时间戳
  *         
  * @param[in]  psn_list：RX-PSN表
  * @param[in]  rxsn：当前收到的包的sn
  *         
  * @return NULL
  */
static void OSAL_OsPortSaveRxPsn(OsPortRxPsn_stu_t *psn_list, uint8_t rxsn)
{
    const uint32_t ticks = OSAL_GetTickCount();
    uint32_t max_past_time = 0;
    uint8_t index = 0;

    for (int i = 0; i < OSPORT_RXPSN_BUFF_LEN; i++)
    {
        uint32_t past_time = OSAL_PastTime(ticks, psn_list[i].timestamp);
        if (max_past_time < past_time)
        {
            max_past_time = past_time;
            index = i;
        }
    }
    psn_list[index].sn = rxsn;
    psn_list[index].timestamp = ticks;
}

/**
  * @brief   这是遍历TX队列的一个回调函数（ACK处理函数），配合OSAL_QueueTraverse()使用
  * @details 详细说明:当收到ACK包时，会遍历整个TX队列，将对应的CMD包删掉
  *         
  * @param[in]  param：接收的ACK命令字和包序号的与结果
  * @param[in]  data：多核通信发送队列单节点（数据域）指针
  * @param[in]  size：数据域的大小
  *         
  * @return 【0】：收到对应的ACK，表示要删除该节点数据域  【1】：收到的ACK非对应，表示要保留该节点数据域
  */
static int OSAL_OsPortAckProcess(uint32_t param, void *data, uint16_t size)
{
    OsPortTxInfo_stu_t *pTx = (OsPortTxInfo_stu_t *)data;

    if (((pTx->packet.cmd << 8) | pTx->packet.psn) == param)
    {
        return 0;//< 当前节点对应的ACK来了，直接删掉
    }
    return 1;
}

 /**
  * @brief   RX处理逻辑
  * @details 详细说明:无
  *         
  * @return NULL
  */
static void OSAL_OsPortRxParse(void)
{
    //OSPORT_LOG("OSAL_OsPortRxParse osport_qty:%d\r\n",osport_qty);
    for (int i=0; i<osport_qty; i++)
    {
        OsPortPaket_stu_t *packet = (OsPortPaket_stu_t *)OSAL_OsPortGetOnPacket(p_osport + i);

        if (packet != NULL)
        {
            if (packet->cmd & OSPORT_CMD_FLAG_ACK)
            {
                /* 收到ACK包：清除TX队列里面对应的PACKET（命令字最高位为1：表示ACK包）  */
                uint8_t cmd = packet->cmd & (~(OSPORT_CMD_FLAG_ACK) & 0xFF);
                OSAL_QueueTraverse(p_osport[i].tx_queue, OSAL_OsPortAckProcess, (cmd << 8) | packet->psn);
            }
            else
            {
                /*  收到命令包：发送ACK */
                OSAL_OsPortSendAck(p_osport[i].hwl_port, packet->cmd | OSPORT_CMD_FLAG_ACK, packet->psn);

                /* 将合法RX包暂存到RX-QUEUE */
                if (OSAL_OsPortCheckRxPsn(p_osport[i].rx_psn, packet->psn, p_osport[i].rx_parse_timestamp) == SUCCESS)
                {
                    OSPORT_LOG("Recv PSN error, rx_psn:%02X\r\n", packet->psn);
                }
                else
                {
                    if (OSAL_QueueSendToBack(p_osport[i].rx_queue, packet, packet->len + 6) == SUCCESS)
                    {
                        rx_queue_len++;
                    }
                    else
                    {
                        OSPORT_LOG("Write rx-queue failed, rx_cmd:%02X, rx_psn:%02X\r\n", packet->cmd, packet->psn);
                    }
                }
                p_osport[i].rx_parse_timestamp = 0;
                p_osport[i].state = SET;
                OSAL_OsPortSaveRxPsn(p_osport[i].rx_psn, packet->psn);
            }
        }
    }
}

/**
  * @brief   这是遍历TX队列的一个回调函数（数据包发送函数），配合OSAL_QueueTraverse()使用
  * @details 详细说明:遍历TX队列，将符合条件的包发送出去
  *         
  * @param[in]  param：遍历时传入的参数，该参数间接表示32位地址，用于指向申请的“OsPort_stu_t”结构地址
  * @param[in]  data：多核通信数据发送队列单节点（数据域）指针
  * @param[in]  size：数据域的大小
  *         
  * @return 【0】：重发次数达到最大，已删除当前节点  【-1】：结束遍历  【1】：继续遍历
  */
static int OSAL_OsPortTxPacket(uint32_t param, void *data, uint16_t size)
{
    OsPortTxInfo_stu_t *pTx = (OsPortTxInfo_stu_t *)data;
    OsPort_stu_t *pOsport = (OsPort_stu_t *)param;
    const uint32_t ticks = OSAL_GetTickCount();

    if (pOsport->state == RESET)
    {
        return 0;
    }

    if ( (pTx->resend_times == 0) ||                                         //< 首次发送直接发
         ((OSAL_PastTime(ticks, pTx->timestamp) > OSPORT_RESEND_INTERVAL) && //< 判断重发间隔
          (OSAL_PastTime(ticks, pOsport->rx_parse_timestamp) > 50)) )        //< 判断当前RX是否正在忙
    {
        /* 重发次数达到最大：删除当前节点 */
        if (pTx->resend_times >= OSPORT_RESEND_TIMES)
        {
            OSPORT_LOG("Tx failed, psn: %02X\r\n", pTx->packet.psn);
            return 0;
        }
        else if (pTx->resend_times > 0)
        {
            OSPORT_LOG("Timeout Resend, times: %d, cmd: %02X, port:%04X, psn: %02X\r\n", pTx->resend_times, pTx->packet.cmd, pOsport->hwl_port, pTx->packet.psn);
        }

        /* 发出数据、记录发送次数和时间戳 */
         if (OSAL_OsPortWriteData2Hwl(pOsport->hwl_port, &(pTx->packet), size - OSAL_OFFSET(OsPortTxInfo_stu_t, packet)) < 0)
         {
             pOsport->state = RESET; //发送失败，标记该端口：未就绪（当端口为BLE通信口时，就会有发送失败的情况）
             return 0;
         }
        pTx->resend_times++;
        pTx->timestamp = OSAL_GetTickCount();
    }

    /* 返回-1表示结束遍历（TX队列里面如果有待发送的HELLO命令或者PACK-0命令，就不允许发出其他命令） */
    return ((pTx->packet.cmd == OSPORT_CMD_HELLO) || (pTx->packet.cmd == OSPORT_CMD_MULTI_PACK_0) || ((pOsport->hwl_port >> 8) == vBLE)) ? -1 : 1;
}

 /**
  * @brief   TX处理逻辑
  * @details 详细说明:无
  *         
  * @return NULL
  */
static void OSAL_OsPortTxProcess(void)
{
    uint16_t len_all = 0;
    //OSPORT_LOG("M OSAL_OsPortTxProcess osport_qty:%d\r\n",osport_qty);
    for (int i=0; i<osport_qty; i++)
    {
#if (!B2B_INT_UART_RX)
        /* 拉唤醒脚 */
        if (((p_osport[i].hwl_wake_pin >> 8) == vPIN) && (OSAL_QueueLenght(p_osport[i].tx_queue, NULL) > 0))
        {
            Device_Write(p_osport[i].hwl_wake_pin, NULL, 0, PIN_MODE_OD_DRIVE);
            Device_Write(p_osport[i].hwl_wake_pin, NULL, 0, 0);
        }
#endif

        /* 遍历TX队列 */
        uint16_t len = OSAL_QueueTraverse(p_osport[i].tx_queue, OSAL_OsPortTxPacket, (uint32_t)(p_osport + i));

#if (!B2B_INT_UART_RX)
        /* 释放唤醒脚 */
        if (((p_osport[i].hwl_wake_pin >> 8) == vPIN) && (len == 0))
        {
            Device_Write(p_osport[i].hwl_wake_pin, NULL, 0, 1);
            Device_Write(p_osport[i].hwl_wake_pin, NULL, 0, PIN_MODE_INPUT_PULLUP);
        }
#endif
        len_all += len;
    }
    tx_queue_len = len_all;
}

/**
  * @brief   pendSV中断处理函数（本函数是硬件中断函数）
  * @details 详细说明:处理OSPORT数据包的收发，调用Device_TrigPendSV()会触发本函数执行
  *         
  * @param[in]  data：缓存数据指针(无意义，用于兼容回调形参格式)
  * @param[in]  len：数据长度(无意义，用于兼容回调形参格式)
  *         
  * @return NULL
  */
static void OSAL_OsPortPendSV(VirtualHardware_enum_t dev, void *data, uint32_t len)
{
    OSAL_OsPortRxParse();
    OSAL_OsPortTxProcess();
}

/**
  * @brief   将待发送的数据包写入TX-QUEUE
  *         
  * @param[in]  port：虚拟设备类型
  * @param[in]  cmd：命令码
  * @param[in]  param1：命令参数1，详细请参考多核通信协议文档
  * @param[in]  param2：命令参数2，详细请参考多核通信协议文档
  * @param[in]  msg：待发送数据地址（指针）
  * @param[in]  len：待发送数据长度
  *         
  * @return NULL
  */
static ErrorStatus OSAL_OsPortLoadTxData(VirtualHardware_enum_t port, uint8_t cmd, uint8_t param1, uint8_t param2, void *msg, uint16_t lenght)
{
    ErrorStatus ret = ERROR;

    for (int i = 0; i < osport_qty; i++)
    {
        if (p_osport[i].state == RESET)
        {
            /**
             * 该端口对应的KOS子设备未就绪，就禁止发出数据
             */
            continue; 
        }

        #ifdef KOS_PARAM_OSPORT_CRYPTO
        if ((cmd != OSPORT_CMD_HELLO) && (cmd != OSPORT_CMD_SYNC) && (p_osport[i].rm == 0 || p_osport[i].rs == 0))
        {
            /**
             * Rm/Rs没有交换完成，只有HELLO和SYNC命令可以发出
             */    
            continue;
        }
        #endif

        if ((port == p_osport[i].hwl_port) || ((port == vNOHARDWARE) && ((p_osport[i].hwl_port >> 8) != vBLE)))
        {
            OsPortTxInfo_stu_t *pTx = NULL;
            uint16_t len = lenght;
            uint8_t insert = 0;

            len += 2;        //< payload区域增加param1、param2    

            #ifdef KOS_PARAM_OSPORT_CRYPTO
            if ((DeviceType_enum_t)(p_osport[i].hwl_port >> 8) != vIPC)
            {
                insert = 2;  //< payload加密传输：在payload末尾多插入2字节crc16
                insert += (16 - ((len + insert) & 0x0F)) & 0x0F;//< 16字节对齐
            }
            #endif

            len += insert;   //< payload区域增加insert

            pTx = OSAL_Malloc(sizeof(OsPortTxInfo_stu_t) + len);
            OSPORT_ASSERT(pTx != NULL);

            memset(pTx, 0, sizeof(OsPortTxInfo_stu_t) + len);
            pTx->resend_times = 0;
            pTx->timestamp = 0;

            /**
             * @brief 填充帧头部分
             */
            pTx->packet.head = 0xFFFF;
            pTx->packet.len = (sizeof(OsPortPaket_stu_t) - 6) + len; //< 数据长度
            pTx->packet.cmd = cmd;                                   //< 命令字
            pTx->packet.psn = ++p_osport[i].tx_psn;                  //< 包编号

            /**
             * @brief 填充payload
             *   1、明文传输payload区域包含ABC三个部分
             *   2、加密传输payload区域包含ABCDE五个部分
             * 
             *   |   A    |   B    |   C   |    D    |   E   |
             *   | param1 | param2 |  msg  |  crc16  |  NUL  |
             */
            pTx->packet.payload[0] = param1;
            pTx->packet.payload[1] = param2;
            memcpy(&pTx->packet.payload[2], msg, len - 2 - insert);
            #ifdef KOS_PARAM_OSPORT_CRYPTO
            pTx->packet.insert = insert;                             //< 插入的数据个数
            if ((DeviceType_enum_t)(p_osport[i].hwl_port >> 8) != vIPC)
            {
                uint16_t crc16 = OSAL_OsPortCrc16(pTx->packet.payload, len - insert);
                memcpy(pTx->packet.payload + len - insert, &crc16, 2);
                OSAL_OsportCryptoData(AES128_ENCRYPTION, cmd, i, pTx->packet.payload, len);
            }
            #endif

            /**
             * @brief 整个帧计算CRC码，放进TX队列
             */
            pTx->packet.crc16 = OSAL_OsPortCrc16(&(pTx->packet.cmd), pTx->packet.len);
            ret = OSAL_QueueSendToBack(p_osport[i].tx_queue, pTx, sizeof(OsPortTxInfo_stu_t) + len);
            OSAL_Free(pTx);
            if (ret == SUCCESS)
            {
				//log_debug_array_ex("tx: ", pTx->packet.payload, 16); //
                OSPORT_LOG("Load tx-data succeed, cmd: %02X,  port: %04X,  psn:%02X\r\n", cmd, p_osport[i].hwl_port, pTx->packet.psn);
                Device_TrigPendSV();
            }
            else
            {
                OSPORT_LOG("Load tx-data failed, cmd: %02X,  port: %04X,  psn:%02X\r\n", cmd, p_osport[i].hwl_port, pTx->packet.psn);
            }
        }
    }
    return ret;
}

/**
  * @brief   处理接收multi-pack
  * @details 详细说明: 将收到的多个multi-pack包组合成一个完成的包
  *         
  * @param[in]  port：端口
  * @param[in]  cmd：命令字
  * @param[in]  data：数据
  * @param[in]  len：数据长度
  *         
  * @return NULL
  */
static void OSAL_ProcessRecvMultiPack(VirtualHardware_enum_t port, uint8_t cmd, uint8_t *data, uint16_t len)
{
#if (OSPORT_PACKET_MAX_LEN >= 1024)
    static OsPortMultiPack0_stu_t pack0;
    static uint16_t recv_len;
    static uint8_t *multi_pack_buffer = NULL;

    if (cmd == OSPORT_CMD_MULTI_PACK_0)
    {
        if (multi_pack_buffer != NULL)
        {
            OSAL_Free(multi_pack_buffer);
            multi_pack_buffer = NULL;
            OSPORT_LOG("Recv multi-pack error\r\n");
        }
        memcpy(&pack0, data + 2, sizeof(pack0));            //< pack0（multi-pack数据域）
        multi_pack_buffer = OSAL_Malloc(pack0.msg_len + 2); //< 给大数据包申请缓存
        OSPORT_ASSERT(multi_pack_buffer != NULL);
        memcpy(multi_pack_buffer, data, 2);                 //< 组件ID/组件昵称
        recv_len = 0;
        OSPORT_LOG("Recv multi-pack0, msg_cmd:%02X, msg_id:%d, msg_len:%d, pack_len:%d, comp_id:%d, alias:%d\r\n",
                    pack0.msg_cmd, pack0.msg_id, pack0.msg_len, pack0.pack_len, data[0], data[1]);
    }
    else if (cmd == OSPORT_CMD_MULTI_PACK_N)
    {
        uint8_t msg_id = data[0];   //< 消息ID
        uint8_t pack_num = data[1]; //< multi-pack包编号
        if (msg_id == pack0.msg_id)
        {
            OSPORT_LOG("Recv multi-pack_n, %d\r\n", pack_num);
            memcpy(multi_pack_buffer + 2 + pack_num * pack0.pack_len, data + 2, len - 2);
            recv_len += len - 2;
        }
        else
        {
            OSPORT_LOG("Recv multi-pack error, msg_id != pack0.msg_id, %d\r\n", pack0.msg_id);
        }

        if (recv_len == pack0.msg_len && recv_len > 0)
        {
            OSPORT_LOG("Recv multi-pack success\r\n");
            OSAL_ProcessOsportCmd(port, pack0.msg_cmd, multi_pack_buffer, pack0.msg_len + 2);
            OSAL_Free(multi_pack_buffer);
            multi_pack_buffer = NULL;
            recv_len = 0;
            pack0.msg_id = 0;
        }
    }
#else
    OSPORT_LOG("Recv error, not support multi-pack.\r\n");
#endif
}

/**
  * @brief   处理RX、TX队列
  * @details 详细说明:无
  *         
  * @return NULL
  */
void OSAL_OsPortProcess(void)
{
    if (OSAL_GetFreeHeapSize() < 512)
    {
        OSPORT_LOG("Memory warning\r\n");
        return;
    }

    //< 处理RX队列的数据
    if (rx_queue_len > 0)
    {
        Device_EnterCritical();
        rx_queue_len = 0;
        Device_ExitCritical();
        
        OsPortPaket_stu_t *packet = (OsPortPaket_stu_t *)OSAL_Malloc(OSPORT_PACKET_MAX_LEN);
        OSPORT_ASSERT(packet != NULL);
        for (int i = 0; i < osport_qty; i++)
        {
            for (;;)
            {
                uint16_t len = OSAL_QueueReceive(p_osport[i].rx_queue, packet);

                if (len == 0)
                {
                    break;
                }
                OSPORT_LOG("Rx cmd: %02x,  port: %04X,  psn:%02X\r\n", packet->cmd, p_osport[i].hwl_port, packet->psn);
                uint16_t payload_len = packet->len - (sizeof(OsPortPaket_stu_t) - 6);
                uint8_t insert = 0;

                #ifdef KOS_PARAM_OSPORT_CRYPTO
                insert = packet->insert;
                if ((DeviceType_enum_t)(p_osport[i].hwl_port >> 8) != vIPC)
                {
                    //< 解密payload
                    OSAL_OsportCryptoData(AES128_DECRYPTION, packet->cmd, i, packet->payload, payload_len);

                    //< 验证解密后的数据是否正确
                    uint16_t crc16;
                    memcpy(&crc16, &(packet->payload[payload_len - insert]), sizeof(crc16));
                    if (OSAL_OsPortCrc16(packet->payload, payload_len - insert) != crc16)
                    {
                        OSPORT_LOG("Decryption error!\r\n\r\n\r\n");
                        continue;
                    }
                }
                #endif

                if (packet->cmd == OSPORT_CMD_MULTI_PACK_0 || packet->cmd == OSPORT_CMD_MULTI_PACK_N)
                {
                    OSAL_ProcessRecvMultiPack(p_osport[i].hwl_port, packet->cmd, packet->payload, payload_len - insert);
                }
                else
                {
                    OSAL_ProcessOsportCmd(p_osport[i].hwl_port, packet->cmd, packet->payload, payload_len - insert);
                }
            }
        }
        OSAL_Free( packet );
    }

    //< 触发PendSV发TX队列的数据
    if (tx_queue_len > 0)
    {
        Device_TrigPendSV();
    }

#if defined(KOS_PARAM_OSPORT_CRYPTO)
    //< OSPORT端口状态管理
    OSAL_OsportStateManage();
#endif
}

/**
  * @brief   通过OSPORT发出一个数据包
  * @details 详细说明:cmd         param1   param2    msg
  *                  TASK-INFO   QTY      0/1       COMP-ID  ALIAS  NV-SIZE  NV-DATA  COMP-ID  ALIAS  NV-SIZE  NV-DATA  ……
  *                  HELLO       0        0         /
  *                  Mbox        comp     alias     data
  *                  publish     comp     alias     data
  *         
  * @param[in]  port：虚拟设备类型
  * @param[in]  cmd：命令码
  * @param[in]  param1：命令参数1，详细请参考多核通信协议文档
  * @param[in]  param2：命令参数2，详细请参考多核通信协议文档
  * @param[in]  msg：待发送数据地址（指针）
  * @param[in]  len：待发送数据长度
  *         
  * @return NULL
  */
ErrorStatus OSAL_OsPortSend(VirtualHardware_enum_t port, uint8_t cmd, uint8_t param1, uint8_t param2, void *msg, uint16_t len)
{
    port = (port == 0) ? ((p_osport != NULL) ? p_osport[0].hwl_port : vNOHARDWARE): port;

#ifdef KOS_PARAM_OSPORT_CRYPTO
    uint8_t insert = (16 - ((len + 4) & 0x0F)) & 0x0F;
    if ((len + 4 + insert) > (OSPORT_PACKET_MAX_LEN - sizeof(OsPortPaket_stu_t)))
#else
    if ((len + 2) > (OSPORT_PACKET_MAX_LEN - sizeof(OsPortPaket_stu_t)))
#endif
    {
    #if (OSPORT_PACKET_MAX_LEN >= 1024)
        OsPortMultiPack0_stu_t pack0 = 
        {
            .msg_cmd  = cmd,
            .msg_id   = multi_pack_msg_id,
            .msg_len  = len,
            .pack_len = ((OSPORT_PACKET_MAX_LEN - sizeof(OsPortPaket_stu_t)) & (~0x0F)), //< 16字节对齐
        };
        
        //< 发出pack_0 --- 减去4是因为OSAL_OsPortLoadTxData里面会插入2字节param和2字节加密crc
        pack0.pack_len -= 4; 
        OSAL_OsPortLoadTxData(port, OSPORT_CMD_MULTI_PACK_0, param1, param2, &pack0, sizeof(pack0));

        //< 发出pack_n
        uint8_t  pack_num = 0;
        uint16_t send_len = 0;
        do
        {
            send_len = (len > pack0.pack_len) ? pack0.pack_len : len;
            OSAL_OsPortLoadTxData(port, OSPORT_CMD_MULTI_PACK_N, multi_pack_msg_id, pack_num, msg, send_len);
            msg = (uint8_t *)msg + send_len;
            len -= send_len;
            pack_num++;
        } while (len > 0);

        //< 消息ID +1 （发送的消息ID永远不为0）
        multi_pack_msg_id = (multi_pack_msg_id == 255) ? (1) : (multi_pack_msg_id + 1);
        return SUCCESS;
    #else
        OSPORT_LOG("Send failed, msg-len toolarge, cmd: %02X,  port: %04X\r\n", cmd, port);
        return ERROR;
    #endif
    }
    else
    {
        return OSAL_OsPortLoadTxData(port, cmd, param1, param2, msg, len);
    }
}

/**
  * @brief   OSPROT 初始化
  * @details 详细说明:读取设备驱动层设置的OSPORT数量,创建相应的数据结构、注册RX回调
  *         
  * @return NULL
  */
void OSAL_OsPortInit(void)
{
    VirtualHardware_enum_t (*hwl_port)[2];

    if (p_osport != NULL)
    {
        return;
    }

    hwl_port = (VirtualHardware_enum_t (*)[2])(Device_GetDeviceCtrlBlock(vCPU)->devParam);
    if (hwl_port == 0)
    {
        return;
    }

    osport_qty = 0;
    for (int i = 0; (i < OSPORT_MAX_QTY) && (hwl_port[i][0] != vNOHARDWARE); i++)
    {
        osport_qty++;
    }
    
    if (osport_qty == 0)
    {
        return;
    }

    p_osport = (OsPort_stu_t *)OSAL_Malloc(sizeof(OsPort_stu_t) * osport_qty);
    OSPORT_ASSERT(p_osport != NULL);
    memset(p_osport, 0, sizeof(OsPort_stu_t) * osport_qty);

    for (int i = 0; i < osport_qty; i++)
    {
        OSPORT_LOG("vHW PORT: %04X, WAKE-PIN: %04X\r\n", hwl_port[i][0], hwl_port[i][1]);

        p_osport[i].rx_queue = OSAL_QueueCreate(OSPORT_QUEUE_LEN);
        p_osport[i].tx_queue = OSAL_QueueCreate(OSPORT_QUEUE_LEN);
        p_osport[i].rx_buffer = OSAL_RingBufferCreate(OSPORT_RX_BUFF_LEN, 1);
        OSPORT_ASSERT((p_osport[i].rx_queue != NULL) && (p_osport[i].tx_queue != NULL) && (p_osport[i].rx_buffer != NULL));

        p_osport[i].hwl_port = hwl_port[i][0];
        p_osport[i].hwl_wake_pin = (hwl_port[i][1] == vPIN_E1) ? vNOHARDWARE : hwl_port[i][1];

        memset(p_osport[i].rx_psn, 0x66, sizeof(p_osport[i].rx_psn));

        Device_RegisteredCB(p_osport[i].hwl_port, OSAL_OsPortRxCallback);
    }

    /*  注册pendSV回调，调用Device_TrigPendSV()时，会触发这个回调执行  */
    Device_RegisteredCB(vCPU_0, OSAL_OsPortPendSV);

#ifdef KOS_PARAM_OSPORT_CRYPTO
    OSAL_OsportAccountInit();
#endif
}

/**
  * @brief   OSPROT WKAE-PIN滤波处理
  * @details 
  *         
  * @param[in]  wake_pin: 虚拟PIN设备
  *         
  * @return  【SUCCEE】：稳定的唤醒信号   【ERROR】：干扰信号
  */
static ErrorStatus OSAL_OsPortWakePinFilter(VirtualHardware_enum_t wake_pin)
{
    uint8_t io = 0x55;

    for (int i = 0; i < 400; i++) // 最多检测200ms时间
    {
        Device_DelayUs(500);
        io <<= 1;
        io |= Device_Read(wake_pin, NULL, 0, 0);
        if (io == 0)
        {
            return SUCCESS; //连续8次读取到低电平
        }
        else if (io == 0xFF)
        {
            return ERROR;
        }
    }
    return ERROR;
}

/**
  * @brief   OSPROT 检查唤醒
  * @details 详细说明:wake_dev是OSPORT里面的设备：就返回SUCCESS
  *         
  * @param[in]  wake_dev: 虚拟设备类型
  *         
  * @return  【SUCCEE】：成功   【ERROR】：失败（错误)
  */
ErrorStatus OSAL_OsPortCheckWakeDevice(VirtualHardware_enum_t wake_dev)
{
    VirtualHardware_enum_t (*hwl_port)[2];

    if (wake_dev == vNOHARDWARE)
    {
        return ERROR;
    }

    hwl_port = (VirtualHardware_enum_t (*)[2])(Device_GetDeviceCtrlBlock(vCPU)->devParam);
    if (hwl_port == 0)
    {
        return ERROR;
    }

    for (int i = 0; (i < OSPORT_MAX_QTY) && (hwl_port[i][0] != vNOHARDWARE); i++)
    {
        if (wake_dev == hwl_port[i][0] || wake_dev == hwl_port[i][1])
        {
            if ((wake_dev >> 8) == vPIN)
            {
                return OSAL_OsPortWakePinFilter(wake_dev);
            }
            else
            {
                return SUCCESS;
            }
        }
    }
    return ERROR;
}

/**
  * @brief   子设备使能 钩子函数
  * @details 应用层应该根据功能需求重新定义该函数
  *         
  * @param[in]  index:子设备编号（其值与cpu.c里面挂载子设备端口的顺序对应）
  *         
  * @return  返回SUCCESS：允许使能从设备
  *          返回ERROR：不允许使能从设备
  */
__attribute__((weak)) ErrorStatus OSAL_SubDevEnableHook(uint8_t index)
{
    return SUCCESS;
}

/**
  * @brief   OSPROT 配置使能、或禁能
  * @details 休眠前会调用DISABLE、唤醒时会调用ENABLE
  *         
  * @param[in]  sta: 使能和禁能
  *         
  * @return  NULL
  */
ErrorStatus OSAL_OsportConfig(FunctionalState sta)
{
    VirtualHardware_enum_t (*hwl_port)[2];

    hwl_port = (VirtualHardware_enum_t (*)[2])(Device_GetDeviceCtrlBlock(vCPU)->devParam);
    if (hwl_port == 0)
    {
        return ERROR;
    }

    for (int i = 0; (i < OSPORT_MAX_QTY) && (hwl_port[i][0] != vNOHARDWARE); i++)
    {
        if (sta == ENABLE)
        {
            if (hwl_port[i][1] == vPIN_E1)
            {
                if (OSAL_SubDevEnableHook(i) == SUCCESS)
                {
                    Device_Write(hwl_port[i][1], NULL, 0, 0);//power-on
                    Device_Enable(hwl_port[i][0]);
                }
                p_osport[i].state = RESET;
            }
            else
            {
                Device_Enable(hwl_port[i][0]);
                if ((DeviceType_enum_t)(hwl_port[i][0] >> 8) != vBLE)
                {
                    p_osport[i].state = SET;
                }
            #ifdef KOS_PARAM_OSPORT_CRYPTO
                else
                {
                    p_osport[i].rm = 0;
                    p_osport[i].rs = 0;
                }
            #endif
            }
        }
        else
        {
            Device_Disable(hwl_port[i][0]);
            if (hwl_port[i][1] == vPIN_E1)
            {
                Device_Write(hwl_port[i][1], NULL, 0, 1); //power-off
            }
            else if ((hwl_port[i][1] >> 8) == vPIN)
            {
                Device_Write(hwl_port[i][1], NULL, 0, 1);
                Device_Write(hwl_port[i][1], NULL, 0, PIN_MODE_INPUT_PULLUP);
            }
        }
    }
    return ((rx_queue_len > 0) && (sta == DISABLE)) ? ERROR : SUCCESS;
}
